#sexprs
from reader import *


def CreateListOfPairs(s,a):
    if s:
        return Pair(s[0],CreateListOfPairs(s[1:],a))
    else:
        return a
    
def isGeneralQuote(sexpr,tag):
    return (sexpr.getType()=='Pair' and str(sexpr.car).lower()==tag and sexpr.cdr.getType()=='Pair' and sexpr.cdr.cdr.getType()=='Nil')
                                  

def isUnquote(sexpr):
    return isGeneralQuote(sexpr,'unquote')

def isUnquoteSplicing(sexpr):
    return isGeneralQuote(sexpr,'unquote-splicing')

    
class AbstractSexpr:
    @staticmethod
    def readFromString(inputStr):
        (m,r) = pSexpr.match(inputStr)
        return (m,r)
    
    def __str__(self):
        pass 
    def getType(self):
        pass
    def printTree(self):
        pass
    
    def code_gen(self):
        pass
    def addToConstantTable(self,constantLookup,constantTable,address):
        return address
    def createTopologicalList(self,constants):
        constants.append(self)
        return []
    
class Void(AbstractSexpr):  
    def __str__(self):
        return '' 
    def printTree(self):
        return 'Void()'
    def code_gen(self):
        code = """
                CALL(MAKE_SOB_VOID);
               """
        return code

class Nil(AbstractSexpr):
    def __init__(self):
        pass
    def __str__(self):
        return '()'
    def getType(self):
        return 'Nil'
    def printTree(self):
        return 'Nil()'
    def code_gen(self):
        code = """
                CALL(MAKE_SOB_NIL);
               """
        return code
    
class Boolean(AbstractSexpr):
    def __init__(self,boolean):
        self.bool = boolean
    def __str__(self):
        return str(self.bool)
    def getType(self):
        return 'Boolean'
    def printTree(self):
        return 'Boolean("'+str(self.bool)+'")'
    def code_gen(self):
        code = "\n"
        if(self.bool):
            asmValue = "BOOL_TRUE"
        else:      
            asmValue = "BOOL_FALSE"
        code = code + "PUSH(IMM(" + asmValue + "));\n"    
        code = code + "CALL(MAKE_SOB_BOOL);\n"
        code = code + "DROP(1);\n"
        return code
    
class Char(AbstractSexpr):
    def __init__(self,char):
        self.char = char
    def __str__(self):
        if(self.char==chr(10)):
            s = 'newline'
        elif(self.char==chr(13)):
            s = 'return'
        elif(self.char==chr(9)):
            s = 'tab'
        elif(self.char==chr(12)):
            s = 'page'        
        else:
            s = self.char    
        return '#'+chr(92)+s
    def getType(self):
        return 'Char'
    def printTree(self):
        return 'Char("'+str(self)+'")'
    def code_gen(self):
        code = "\n"
        code = code + "PUSH(IMM("+ str(ord(self.char)) +"));\n"    
        code = code + "CALL(MAKE_SOB_CHAR);\n"
        code = code + "DROP(1);\n"
        return code
    def addToConstantTable(self,constantLookup,constantTable,address):
        struct = [ "T_CHAR" , ord(self.char) ]
        
        constantTable.append( [address , struct , str(self)] )
        constantLookup[str(self)] = len(constantTable)-1
        
        return address + 2
        
    
    
class AbstractNumber(AbstractSexpr):
    def getType(self):
        return 'Number'

class Integer(AbstractNumber):
    def __init__(self,num):
        self.num = num
    def __str__(self):
        return str(self.num)
    def printTree(self):
        return 'Integer("'+str(self)+'")'
    def code_gen(self):
        code = "\n"
        code = code + "PUSH(IMM("+ self.num +"));\n"    
        code = code + "CALL(MAKE_SOB_INTEGER);\n"
        code = code + "DROP(1);\n"
        return code
    def addToConstantTable(self,constantLookup,constantTable,address):
        struct = [ "T_INTEGER" , self.num ]
        
        constantTable.append( [address , struct , str(self)] )
        constantLookup[str(self)] = len(constantTable)-1
         
        return address + 2   
    
class Fraction(AbstractNumber):
    def __init__(self,numerator,denominator):
        self.numerator = numerator.num
        self.denominator = denominator
    def __str__(self):
        return str(self.numerator) + '/' + str(self.denominator)
    def printTree(self):
        return 'Fraction("'+str(self)+'")'
    def code_gen(self):
        code = "\n"
        code = code + "PUSH(IMM("+ str(self.denominator) +"));        \t\t// 2 param - denominator \n"    
        code = code + "PUSH(IMM("+ str(self.numerator) +"));          \t\t// 1 param - numerator  \n"
        code = code + "CALL(MAKE_SOB_FRACTION);\n"
        code = code + "DROP(2);\n"
        return code
    def addToConstantTable(self,constantLookup,constantTable,address):
        struct = [ "T_FRACTION" , self.numerator , self.denominator ]
        
        constantTable.append( [address , struct , str(self)] )
        constantLookup[str(self)] = len(constantTable)-1
        
        return address + 3

class String(AbstractSexpr):
    def __init__(self,string):
        self.string = string
    def __str__(self):
        return '"'+str(self.string)+'"'
    def getType(self):
        return 'String'
    def printTree(self):
        return 'String("'+str(self)+'")'
    def code_gen(self):
        code = "\n"
        length = len(self.string)
        i=0
        while(i<length):
            code = code + "PUSH(IMM("+ self.string[i] +"));\n"
            i = i+1;    
        code = code + "CALL(MAKE_SOB_STRING);\n"
        code = code + "DROP("+length+");\n"
        return code
    def addToConstantTable(self,constantLookup,constantTable,address):
        struct = ["T_STRING" , len(self.string) ]
        nextAddr = address + 2 
        for c in self.string:
            struct.append(ord(c))
            nextAddr = nextAddr + 1 
         
        constantTable.append( [address , struct , str(self)] )   
        constantLookup[str(self)] = len(constantTable)-1
            
        return nextAddr
    
class Symbol(AbstractSexpr):
    def __init__(self,symbol):
        self.symbol = symbol.upper()
    def __str__(self):
        return self.symbol
    def getType(self):
        return 'Symbol'
    def printTree(self):
        return 'Symbol("'+str(self)+'")'
    def __eq__(self,y):
        return self.symbol==y.symbol
    def addToConstantTable(self,constantLookup,constantTable,address):
        stringRep = String(self.symbol)
        if not constantLookup.get(str(stringRep)):          #only if there is not already an existing STRING like this in constantTable! - in order to prevent duplication 
                stringRepPointer = address      # the string will be created on the spot
                address = stringRep.addToConstantTable(constantLookup,constantTable,address)
        else:
                stringRepPointer = constantLookup[str(stringRep)]    
                        
        struct = ["T_SYMBOL" , stringRepPointer]
        
        constantTable.append( [address , struct , str(self)] )           
        constantLookup[str(self)] = len(constantTable)-1
        return address + 2
    
class Pair(AbstractSexpr):
    def __init__(self,car,cdr):
        self.car = car
        self.cdr = cdr
    def __str__(self):
        #alterate way of printing:
        #return '(' + str(self.car) + ' ' + str(self.cdr) + ')'
        #THIS ONE IS NICER, BUT ITS NOT WORKING RIGHT DUE TO QUASIQOUTE TAGPARSING, WHICH RISE THE POSIBILITY OF SCHEMEEXPR INSIDE PAIR,
        # AND NOT ONLY SEXPR:
        s = '(' + str(self.car)
        nextPair = self.cdr
        while(nextPair.getType()=='Pair'):
            s = s + ' ' + str(nextPair.car)
            nextPair = nextPair.cdr
        if(nextPair.getType()!='Nil'):
            s = s + ' . ' + str(nextPair)
        s = s + ')'
        return s
    def getType(self):
        return 'Pair'
    def printTree(self):
        return 'Pair('+self.car.printTree()+','+self.cdr.printTree()+')'
    
    def addToConstantTable(self,constantLookup,constantTable,address):
        constants =[]
        pairs = self.createTopologicalList(constants)
        
        for const in constants:
            if not constantLookup.get(str(const)):          #only if there is not already an existing constant like this in constantTable! - in order to prevent duplication
                address = const.addToConstantTable(constantLookup,constantTable,address)
                
        for p in pairs:
            if not constantLookup.get(str(p)):          #only if there is not already an existing constant like this in constantTable! - in order to prevent duplication
                constTableIndex = constantLookup[str(p.car)]
                carAddress = constantTable[constTableIndex][0]
                constTableIndex = constantLookup[str(p.cdr)]
                cdrAddress = constantTable[constTableIndex][0] 
                struct = ["T_PAIR",carAddress,cdrAddress]     
                constantTable.append( [address , struct , str(p)] )   
                constantLookup[str(p)] = len(constantTable)-1
                address = address + 3
        return address    
             
        
    def createTopologicalList(self,constants):
        constants.append(self.car)
        pairs = self.cdr.createTopologicalList(constants)
        pairs.append(self)
        return pairs
        
            
        
        
    
class Vector(AbstractSexpr):
    def __init__(self,vec):
        self.vec = vec
    def __str__(self):
        ans = '#('
        for item in self.vec:
                ans = ans + ' ' + str(item)
        ans = ans + ')'       
        return ans
    def getType(self):
        return 'Vector'
    def printTree(self):
        return 'Vector("'+str(self)+'")'  
    def toList(self):
        return CreateListOfPairs(self.vec,Nil())
    def addToConstantTable(self,constantLookup,constantTable,address):
        #adding each item in vector to the constant table:
        itemsMemPointers = []
        for item in self.vec:
            if not constantLookup.get(str(item)):          #only if there is not already an existing constant like this in constantTable! - in order to prevent duplication 
                itemsMemPointers.append(item.addToConstantTable(constantLookup,constantTable,address))
#                address = address + 2
            else:
                i = constantLookup[str(item)]
                itemsMemPointers.append(constantTable[i][0])    
        #adding the vector object to the constant list        
        struct = ["T_VECTOR" , len(self.vec) ]
        nextAddr = address + 2 
        for pointer in itemsMemPointers:
            struct.append(pointer)
            nextAddr = nextAddr + 1 
         
        constantTable.append( [address , struct , str(self)] )   
        constantLookup[str(self)] = len(constantTable)-1
            
        return nextAddr
    
